/*
AcessOS/Acess Basic v1
Configuration Settings
*/
#include <acess.h>
#include <config.h>

// === GLOBALS ===
char	*gsInitProgram;
char	*gsInitScript;
char	*gsRootDevice;
char	*gsBootDevice;

// === PROTOTYPES ===
int GetString(char *Str, char **Dest);

// === CODE ===
/**
 * \fn void Config_LoadMBoot(mboot_info *mboot)
 * \brief Load config from multiboot information
 */
void Config_LoadMBoot(mboot_info *mboot)
{
	 int	i, argc, j;
	char	*str, *start;
	char	*argv[18];
	char	*newInit = NULL;
	char	*newInitS = NULL;
	char	*newRootDev = NULL;
	
	str = (char*)(mboot->cmdline += 0xC0000000);
	LogF(" MBoot Command Line: '%s'\n", str);
	Config_LoadDefaults();
	
	// Create `argc` and `argv`
	start = str;	argc = 0;
	for( i = 0; str[i]; i ++)
	{
		if(str[i] == ' ')
		{
			argv[argc++] = start;
			str[i] = '\0';
			start = str+i+1;
		}
	}
	argv[argc++] = start;
	
	// Parse Arguments
	for( i = 1; i < argc; i ++ )
	{
		// Check for Equals
		for( j = 0; argv[i][j] && argv[i][j] != '='; j ++ );
		if(argv[i][j] == '\0')	continue;
		
		argv[i][j] = '\0';
		if( strcmp("INIT", argv[i]) == 0 )		newInit = argv[i]+j+1;
		else if( strcmp("SCRIPT", argv[i]) == 0 )	newInitS = argv[i]+j+1;
		else if( strcmp("ROOT", argv[i]) == 0 )	newRootDev = argv[i]+j+1;
		else
			WarningEx("CFG", "Unknown boot option '%s', Arg '%s'\n", argv[i], argv[i]+j+1);
	}
	
	// Apply New Settings
	if(newInit != NULL) {
		gsInitProgram = malloc( strlen(newInit) + 1 );
		strcpy(gsInitProgram, newInit);
	}
	if(newInitS != NULL) {
		gsInitScript = malloc( strlen(newInitS) + 1 );
		strcpy(gsInitScript, newInitS);
	}
	if(newRootDev != NULL) {
		gsRootDevice = malloc( strlen(newRootDev) + 1 );
		strcpy(gsRootDevice, newRootDev);
	}
}

void Config_LoadDefaults()
{
	gsInitProgram = "/Mount/Root/bin/sh2.axe";
	gsInitScript = "/Mount/fd0/Acess/BootConf.cfg";
	gsRootDevice = "/Devices/ide/A1";
	gsBootDevice = "/Devices/fdd/0";
}

void Config_PrintConfig()
{
	LogF("System Configuration:\n");
	LogF(" INITS = '%s'\n", gsInitScript);
	LogF(" INIT = '%s'\n", gsInitProgram);
	LogF(" ROOT = '%s'\n", gsRootDevice);
	LogF(" BOOT = '%s'\n", gsBootDevice);
}

/**
 * \fn void Config_ExecuteScript()
 */
void Config_ExecuteScript()
{
	 int	fp;
	 int	fLen = 0;
	 int	i = 0, lineStart;
	char	*sArg1, *sArg2, *sArg3;
	char	*fData;
	
	// Open Script
	fp = vfs_open(gsInitScript, VFS_OPENFLAG_READ);
	if(fp == -1) {
		WarningEx("CFG", "Passed script '%s' does not exist", gsInitScript);
		return;
	}
	
	vfs_seek(fp, 0, SEEK_END);
	fLen = vfs_tell(fp);
	vfs_seek(fp, 0, SEEK_SET);
	fData = malloc(fLen+1);
	vfs_read(fp, fLen, fData);
	fData[fLen] = '\0';
	vfs_close(fp);
	
	// Read Script
	while(i < fLen)
	{
		sArg1 = sArg2 = sArg3 = NULL;
		
		lineStart = i;
		// Clear leading whitespace and find empty lines
		while(i < fLen && (fData[i] == ' ' || fData[i]=='\t'))	i ++;
		if(i == fLen)	break;
		if(fData[i] == '\n') {
			i++;
			continue;
		}
		
		// Comment
		if(fData[i] == ';' || fData[i] == '#') {
			while(i < fLen && fData[i] != '\n')	i ++;
			i ++;
			continue;
		}
		
		// Commands
		// - Mount
		if(strncmp("mount ", fData+i, 6) == 0) {
			i += 6;
			i += GetString(fData+i, &sArg1);
			if(!sArg1)	goto read2eol;
			i += GetString(fData+i, &sArg2);
			if(!sArg2)	goto read2eol;
			i += GetString(fData+i, &sArg3);
			if(!sArg3)	goto read2eol;
			//LogF("[CFG ] Mount '%s' to '%s' (%s)\n", sArg1, sArg2, sArg3);
			VFS_MountName(sArg1, sArg2, "", sArg3);
		}
		// - Load Module
		else if(strncmp("module ", fData+i, 6) == 0) {
			i += 7;
			i += GetString(fData+i, &sArg1);
			if(!sArg1)	goto read2eol;
			//LogF("[CFG ] Load Module '%s'\n", sArg1);
			Module_Load(sArg1);
		}
		// - Load Module
		else if(strncmp("edimod ", fData+i, 6) == 0) {
			i += 7;
			i += GetString(fData+i, &sArg1);
			if(!sArg1)	goto read2eol;
			LogF("[CFG ] Load EDI Module '%s'\n", sArg1);
			Edi_LoadDriver(sArg1);
		}
		// - Symlink
		else if(strncmp("symlink ", fData+i, 7) == 0) {
			i += 8;
			i += GetString(fData+i, &sArg1);
			if(!sArg1)	goto read2eol;
			i += GetString(fData+i, &sArg2);
			if(!sArg2)	goto read2eol;
			LogF("[CFG ] Symlink '%s' pointing to '%s'\n", sArg1, sArg2);
			vfs_symlink(sArg1, sArg2);
		}
		// - New Directory
		else if(strncmp("mkdir ", fData+i, 5) == 0) {
			i += 6;
			i += GetString(fData+i, &sArg1);
			if(!sArg1)	goto read2eol;
			LogF("[CFG ] New Directory '%s'\n", sArg1);
			vfs_mknod(sArg1, VFS_FFLAG_DIRECTORY);
		}
		else {
			WarningEx("CFG", "Unknown configuration command, Line: '%s'", fData+i);
			goto read2eol;
		}
	read2eol:
		if(sArg1)	free(sArg1);
		if(sArg2)	free(sArg2);
		if(sArg3)	free(sArg3);
		// Skip to EOL
		while(i < fLen && fData[i] != '\n')	i++;
		i ++;	// Skip \n
	}
	free(fData);
}

/**
 * \fn int GetString(char *Str, char **Dest)
 * \brief Gets a string from another
 * \note Destructive
 * \param Str	Input String
 * \param Dest	Pointer to output pointer
 * \return Characters eaten from input
 */
int GetString(char *Str, char **Dest)
{
	 int	pos = 0;
	 int	start = 0;
	 int	len;
	 
	//LogF("GetString: (Str='%s', Dest=0x%x)\n", Str, Dest);
	 
	while(Str[pos] == ' ' || Str[pos] == '\t')	pos++;
	if(Str[pos] == '\n' || Str[pos] == '\0') {
		*Dest = NULL;
		return pos;
	}
	
	// Quoted String
	if(Str[pos] == '"')
	{
		pos ++;
		start = pos;
		while(Str[pos] != '"')	pos++;
		
		len = pos - start;
		*Dest = malloc( len + 1 );
		memcpy( *Dest, Str+start, len );
		(*Dest)[len] = '\0';
		
		//LogF("GetString: RETURN *Dest = '%s'\n", *Dest);
		
		pos++;
		return pos;
	}
	
	// Non-Quoted String - Whitespace deliminated
	start = pos;
	while(Str[pos] != ' ' && Str[pos] != '\t' && Str[pos] != '\n')	pos++;
	
	len = pos - start;
	//LogF(" GetString: len = %i\n", len);
	*Dest = malloc( len + 1 );
	memcpy( *Dest, Str+start, len );
	(*Dest)[len] = '\0';
	
	//LogF("GetString: RETURN *Dest = '%s'\n", *Dest);
	
	return pos;
}
